package com.tencent.wework;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.qdc.datacenters.exception.QwMsgPullException;
import com.qdc.datacenters.service.impl.ThreadFactoryImpl;
import com.qdc.datacenters.utils.RSAUtil;
import com.qdc.datacenters.utils.ThreadUtils;
import lombok.extern.slf4j.Slf4j;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import static java.util.concurrent.TimeUnit.MILLISECONDS;

/**
 * @author danmo
 * @date 2024-02-28 09:57
 **/
@Slf4j
public class FinanceService {

    /**
     * 超时时间，单位秒
     */
    private final long timeout = 5;


    /**
     * 企业id
     */
    private String corpId;

    /**
     * 会话存档密钥
     */
    private String secret;

    /**
     * 会话私钥
     */
    private Map<Integer, String> encryMap;

    /**
     * 代理
     */
    private String proxy;

    /**
     * 代理密码
     */
    private String passwd;

    /**
     * 一次拉取的消息条数，最大值1000条，超过1000条会返回错误
     */
    private final long LIMIT = 1000L;

    private final ExecutorService msgPullExecutorService = new ThreadPoolExecutor(5, 10, 10L, MILLISECONDS, new ArrayBlockingQueue<>(1024), new ThreadFactoryImpl("ScheduleMsgPullThread_"), new RejectedExecutionHandler() {
        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            if (!executor.isShutdown()) {
                executor.getQueue().poll();
                executor.execute(r);
            }
        }
    });


    public FinanceService(String corpId, String secret, Map<Integer, String> encryMap) {
        this(corpId, secret, encryMap, "", "");
    }

    /**
     * 构造器
     *
     * @param corpId     企业id
     * @param secret     会话存档密钥
     * @param encryMap 会话私钥
     */
    public FinanceService(String corpId, String secret, Map<Integer, String> encryMap, String proxy, String passwd) {
        this.corpId = corpId;
        this.secret = secret;
        this.encryMap = encryMap;
        this.proxy = proxy;
        this.passwd = passwd;
    }

    /**
     * 初始化
     */
    private long initSDK() {
        long sdk = Finance.NewSdk();
        int ret = Finance.Init(sdk, corpId, secret);
        if (ret != 0) {
            Finance.DestroySdk(sdk);
            throw new QwMsgPullException("init sdk err ret " + ret);
        }
        return sdk;
    }

    /**
     * 拉取聊天记录
     *
     * @param seq 消息的seq值，标识消息的序号
     */
    public void getChatData(long seq, Consumer<JSONObject> consumer) {
        long sdk = initSDK();
        long slice = Finance.NewSlice();
        int ret = Finance.GetChatData(sdk, seq, LIMIT, proxy, passwd, timeout, slice);
        if (ret != 0) {
            log.info("getChatData ret " + ret);
            return;
        }
        String content = Finance.GetContentFromSlice(slice);
        JSONArray chatDataArr = JSONObject.parseObject(content).getJSONArray("chatdata");
        log.info("开始执行数据解析:------------");
        if (CollectionUtil.isNotEmpty(chatDataArr)) {
            msgPullExecutorService.submit(() ->{
                chatDataArr.stream().map(data -> (JSONObject) data).forEach(data -> {
                    try {
                        //获取私钥
                        PrivateKey privateKeyObj = RSAUtil.getPrivateKey(encryMap.get(data.getInteger("publickey_ver")));
                        String str = RSAUtil.decryptRSA(data.getString("encrypt_random_key"), privateKeyObj);
                        //初始化参数slice
                        long msg = Finance.NewSlice();
                        //解密
                        int dateRet = Finance.DecryptData(sdk, str, data.getString("encrypt_chat_msg"), msg);
                        if (dateRet != 0) {
                            throw new QwMsgPullException("getChatData dateRet "+dateRet);
                        }
                        String jsonDataStr = Finance.GetContentFromSlice(msg);
                        log.info("数据解析:------------{}", jsonDataStr);
                        JSONObject realJsonData = JSONObject.parseObject(jsonDataStr);
                        realJsonData.put("seq", data.getLong("seq"));
                        realJsonData.put("corpId", this.corpId);
                        consumer.accept(realJsonData);
                        Finance.FreeSlice(msg);
                    } catch (Exception e) {
                        log.error("getChatData seq:{} ,e:{}" ,seq,e.getMessage(),e);
                    }
                });
            });
            log.info("数据解析完成:------------");
        }
        Finance.FreeSlice(slice);
        Finance.DestroySdk(sdk);
    }




    public  boolean getMediaData(String sdkFileId, String savefile) {
        long sdk = initSDK();
        String indexbuf = "";
        while (true) {
            long mediaData = Finance.NewMediaData();
            int ret = Finance.GetMediaData(sdk, indexbuf, sdkFileId, proxy, passwd, 5 * 60, mediaData);
            if (ret != 0) {
                log.info("getMediaData ret:" + ret);
                Finance.FreeMediaData(mediaData);
                return false;
            }
            log.info("getMediaData outIndex len:{}, data_len:{}, is_finish:{}",Finance.GetIndexLen(mediaData),Finance.GetDataLen(mediaData), Finance.IsMediaDataFinish(mediaData));
            try {
                File file = new File(savefile);
                if (!file.getParentFile().exists()) {
                    boolean mkdirs = file.getParentFile().mkdirs();
                    if(mkdirs){
                        boolean newFile = file.createNewFile();
                    }
                }
                FileOutputStream outputStream = new FileOutputStream(file, true);
                outputStream.write(Finance.GetData(mediaData));
                outputStream.close();
            } catch (Exception e) {
                log.error(e.getMessage(),e);
                throw new QwMsgPullException(e.getMessage());
            }
            if (Finance.IsMediaDataFinish(mediaData) == 1) {
                Finance.FreeMediaData(mediaData);
                break;
            } else {
                indexbuf = Finance.GetOutIndexBuf(mediaData);
                Finance.FreeMediaData(mediaData);
            }
        }
        Finance.DestroySdk(sdk);
        return true;
    }

    public void shutdown(){
        log.info("FinanceService线程池开始注销");
        ThreadUtils.shutdownAndAwaitTermination(msgPullExecutorService);
    }
}
